import { 
  AmbientLight,
  BoxGeometry,
  CatmullRomCurve3,
  DoubleSide,
  Mesh,
  MeshPhongMaterial,
  MeshStandardMaterial,
  PerspectiveCamera,
  PlaneGeometry,
  RepeatWrapping,
  Scene, 
  SpotLight, 
  TextureLoader, 
  TubeBufferGeometry, 
  Vector3, 
  WebGLRenderer
} from "three";
import { TrackballControls } from 'three/examples/jsm/controls/TrackballControls';
import Stats from 'three/examples/jsm/libs/stats.module';
import TWEEN from '@tweenjs/tween.js';
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { GUI } from 'dat.gui'

const scene = new Scene();
const camera = new PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
camera.position.set(-80, 50, 110);
const renderer = new WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);

scene.add(new AmbientLight(0xffffff));

const spotLight = new SpotLight(0xffffff);
spotLight.position.set(0, 100, 0);
spotLight.castShadow = true;
scene.add(spotLight);

const textureLoader = new TextureLoader();
const planeGeometry = new PlaneGeometry(200, 200);
const planeMaterial = new MeshPhongMaterial({
  color: 0xffffff,
});
planeMaterial.map = textureLoader.load('/factory/map.png');
planeMaterial.map.wrapS = RepeatWrapping;
planeMaterial.map.wrapT = RepeatWrapping;
const plane = new Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = Math.PI/-2;
scene.add(plane);

const boxGeometry = new BoxGeometry(2, 4, 2);
const boxMaterial = new MeshStandardMaterial({
  color: 0xff9900
});
const box = new Mesh(boxGeometry, boxMaterial);
box.position.set(0, 2, 0);
scene.add(box);


const gltfLoader = new GLTFLoader();
gltfLoader.setCrossOrigin( 'anonymous' );
gltfLoader.setDRACOLoader(new DRACOLoader());

gltfLoader.load(
  '/example/test1.gltf',
  gltf => {
    let mesh = gltf.scene;
    let mate = new MeshStandardMaterial({
      color: 0x777777,
      metalness: 0.7,
      roughness: 0.5,
      transparent: true,
      opacity: 0.5,
      side: DoubleSide
    })

    mesh.traverse((o: any) => {
      if (o.isMesh) o.material = mate
    })

    mesh.scale.set(1.26, 1.26, 1.26);

    scene.add(mesh);
  }
);

const points = [
  new Vector3(100, 0, 26.5),
  new Vector3(49, 0, -1),
  new Vector3(1, 0, -20),
  new Vector3(-58, 0, -31),
  new Vector3(-100, 0, -32),
]
const curve = new CatmullRomCurve3(points);
const geom = new TubeBufferGeometry(curve, 100, 0.01, 8, false);
const line = new Mesh(geom);
scene.add( line );

const trackballControls = new TrackballControls(camera, renderer.domElement);

// @ts-ignore
const stats = new Stats();
stats.showPanel(0);
document.body.appendChild(stats.dom);

const controls = {
  flow: false,
  time: 10000,
  go() {
    animate()
  }
}
const gui = new GUI();
gui.add(controls, 'flow').name('第一人称视角').onChange(val => {
  if (!val) {
    camera.position.set(-80, 50, 100);
  }
})
gui.add(controls, 'time', 1000, 20000).name('时间');
gui.add(controls, 'go')

function animate() {
  new TWEEN.Tween({ value: 0 }).to({ value: 1000 }, controls.time)
    .onUpdate( ({ value }) => {
      // TODO: 可优化
      let {x, y, z} = curve.getPoints(1000)[~~value];
      let at = curve.getPoints(1000)[~~value +1] || new Vector3(0, 0, 0);
      box.position.set(x, y, z);

      if (controls.flow) {
        camera.position.set(x, 0.02, z);
        camera.lookAt(at);
      }
    })
    .start()
}

function render() {
  stats.update();
  TWEEN.update();

  if (!controls.flow) {
    trackballControls.update();
  }

  requestAnimationFrame(render);
  renderer.render(scene, camera);
}

render();